Skip to content

feat(webauthn): largeBlob read via authenticatorLargeBlobs#206

Open
AlfioEmanueleFresta wants to merge 3 commits into
masterfrom
feat/webauthn-largeblob-storage
Open

feat(webauthn): largeBlob read via authenticatorLargeBlobs#206
AlfioEmanueleFresta wants to merge 3 commits into
masterfrom
feat/webauthn-largeblob-storage

Conversation

@AlfioEmanueleFresta
Copy link
Copy Markdown
Member

@AlfioEmanueleFresta AlfioEmanueleFresta commented May 10, 2026

Implements the read half of the WebAuthn L3 largeBlob extension end to end. Until now the library could request a per-credential key from the authenticator but had no way to actually fetch and decrypt the stored blob, so the read output was never populated.

After an assertion succeeds and yields a per-credential AES key, the platform paginates authenticatorLargeBlobs(get), AES-256-GCM-authenticates each on-device entry under that key, and RFC 1951 raw-deflate decompresses the plaintext into the assertion response. Read failures are non-fatal and surface as an absent blob, per WebAuthn L3 §10.1.5.

Spec compliance

  • chunk size honours maxFragmentLength = maxMsgSize - 64 from GetInfo (CTAP 2.1 §6.10.2)
  • origSize is capped at 1 MiB to bound platform allocation
  • per-entry structural problems are skipped, not propagated, since the on-device array is shared across credentials

Tests

  • unit tests cover AES-256-GCM round-trip, wrong-key skip, multi-entry selection, corrupted-trailer rejection, malformed-entry skip, and oversized-origSize skip
  • a mock-channel end-to-end test exercises the full read path
  • a new integration test against the virt fido-authenticator registers a credential, plants an encrypted blob via the CTAP set, and asserts the platform fetches and decrypts on the next assertion

Follow-ups

The write half lands in the next PR: chunked CTAP upload with pinUvAuthParam, fetch-modify-write of the on-device array, and the WebAuthn largeBlob.write extension wiring.

Spec references

@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the fix/webauthn-largeblob-key-disclosure branch from 4f484fb to 63fd2d2 Compare May 10, 2026 20:26
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-largeblob-storage branch from 1129c2d to 5a50879 Compare May 10, 2026 20:43
AlfioEmanueleFresta added a commit that referenced this pull request May 12, 2026
Per review on #198: keep the per-credential largeBlobKey only on the
CTAP-level Ctap2GetAssertionResponse. Surfacing it on the public
Assertion struct gives callers a foot-gun to forward straight to the
RP, which is exactly the disclosure this PR is meant to prevent. The
follow-up authenticatorLargeBlobs PR (#206) can read the key directly
off the CTAP response.
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the fix/webauthn-largeblob-key-disclosure branch from 63fd2d2 to 34571aa Compare May 12, 2026 17:46
AlfioEmanueleFresta added a commit that referenced this pull request May 12, 2026
Per review on #198: keep the per-credential largeBlobKey only on the
CTAP-level Ctap2GetAssertionResponse. Surfacing it on the public
Assertion struct gives callers a foot-gun to forward straight to the
RP, which is exactly the disclosure this PR is meant to prevent. The
follow-up authenticatorLargeBlobs PR (#206) can read the key directly
off the CTAP response.
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the fix/webauthn-largeblob-key-disclosure branch from 34571aa to 5388273 Compare May 12, 2026 17:47
AlfioEmanueleFresta added a commit that referenced this pull request May 12, 2026
Per [WebAuthn L3 sec. 10.1.5 (largeBlob
extension)](https://www.w3.org/TR/webauthn-3/#sctn-large-blob-extension),
the relying party expects the `blob` output to be the decrypted
plaintext blob payload, fetched by the platform via the CTAP
[`authenticatorLargeBlobs`](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorLargeBlobs)
command using the per-credential `largeBlobKey` as an AES-256-GCM key.
The library was instead writing the raw `largeBlobKey` into `blob` and
never calling `authenticatorLargeBlobs`. That means an RP receives the
AES key itself (not the blob), and if the RP can also read the device's
`largeBlobArray` (publicly readable region of the authenticator over
CTAP), it can decrypt and forge entries.

Until `authenticatorLargeBlobs` is wired up (follow-up PR #206), the
safe behaviour is to set `large_blob.blob = None`. The CTAP-level model
keeps the field so the follow-up can use it.

## Changes

- Stop routing `largeBlobKey` into the WebAuthn `large_blob.blob`
output.
- The CTAP-level `Ctap2GetAssertionResponse.large_blob_key` remains so
the next PR can use it.
- Regression test asserts the WebAuthn response no longer contains the
key.

## References

- [WebAuthn L3 sec. 10.1.5: Large blob storage extension
(`largeBlob`)](https://www.w3.org/TR/webauthn-3/#sctn-large-blob-extension)
- [CTAP 2.1 sec. 6.10: `authenticatorLargeBlobs`
(0x0C)](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorLargeBlobs)
Base automatically changed from fix/webauthn-largeblob-key-disclosure to master May 12, 2026 17:51
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-largeblob-storage branch from 5a50879 to 33be166 Compare May 12, 2026 18:41
@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as ready for review May 12, 2026 18:41
@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as draft May 12, 2026 18:45
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-largeblob-storage branch from 33be166 to 82e3197 Compare May 19, 2026 18:40
@AlfioEmanueleFresta AlfioEmanueleFresta changed the title feat(webauthn): authenticatorLargeBlobs + LargeBlobStorage trait feat(webauthn): largeBlob read via authenticatorLargeBlobs May 19, 2026
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-largeblob-storage branch from 82e3197 to c70b88a Compare May 19, 2026 18:54
Implements the wire-level model and protocol method for CTAP 2.1
`authenticatorLargeBlobs` (command code 0x0C, spec §6.10). This is the
device-side primitive the platform uses to fetch and update the
authenticator's serialized largeBlobArray.

Includes only the `get` request shape so far; `set` is reserved for a
follow-up that will also handle the pinUvAuthParam binding required for
writes.

Refs: CTAP 2.2 §6.10.
Adds the read-path helper for the WebAuthn L3 largeBlob extension. After
get_assertion returns a largeBlobKey, the platform paginates
authenticatorLargeBlobs(get), AES-256-GCM-authenticates each entry under
the per-credential key, and RFC 1951 raw-deflate decompresses the
plaintext into unsigned_extensions_output.large_blob.blob.

Read failures surface as blob absent, per WebAuthn L3 sec 10.1.5. The
chunk size honours maxFragmentLength = maxMsgSize - 64 from GetInfo.
origSize is capped at 1 MiB to bound platform allocation. Per-entry
structural problems are skipped, not propagated, since the on-device
array is shared across credentials.
Enables largeBlobs storage on the virt fido-authenticator and adds an
end-to-end test: registers a credential, plants an encrypted entry
directly via authenticatorLargeBlobs(set), then asserts
webauthn_get_assertion with largeBlob.read surfaces the plaintext.

Requires the chunked feature on fido-authenticator to lift the 1024-byte
single-message cap on the device-wide array, and a non-zero max_msg_size
in the virt config so the per-chunk length check passes.
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-largeblob-storage branch from c70b88a to 3e87620 Compare May 19, 2026 18:59
@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as ready for review May 19, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant